dojo.provide("demo.widget.IconComboBox");

dojo.require("dijit.form.ComboBox");

/* @Override from dojo-1.4.3 */
dojo.declare(
    "demo.widget.IconComboBox",
    dijit.form.ComboBox,
    {
        // summary:
        //      A ComboBox that displays options with icons in the drop down menu.
        //
        // description:
        //      IconComboBox extends the core dijit ComboBox to provide a drop down list with icon options.
        //        1. Added iconClass(item, store) function - to display customized icons for options in dropdown list.<br />
        //        2. Fixed the defect of not highlighting all matches in highlight=all mode for ComboBox prior to Dojo 1.6.0.<br />
        //        3. TODO: validation with tooltip error message.<br />
        // tags:
        //      protected

        // dropDownClass: [protected extension] String
        //      Name of the dropdown widget class used to select a date/time.
        //      Subclasses should specify this.
        dropDownClass: "demo.widget._IconComboBoxMenu",

        // iconAttr: String?
        //      The icons in the drop down list come from this attribute in the
        //      dojo.data items.
        //      If not specified, there will be no icon.
        iconAttr: "name",

        /* @Override */
        closeDropDown: function() {
            this._hideResultList(); // NOTE: backward compability to 1.4.3
        },

        /* @Override */
        _startSearch: function(/*String*/ key) {
            // summary:
            //      Starts a search for elements matching key (key=="" means to return all items),
            //      and calls _openResultList() when the search completes, to display the results.
            if (!this.dropDown) {
                var popupId = this.id + "_popup";
                var dropDownConstructor = dojo.getObject(this.dropDownClass, false);
                this.dropDown = new dropDownConstructor({
                    onChange: dojo.hitch(this, this._selectOption),
                    id: popupId,
                    dir: this.dir
                });
                this._popupWidget = this.dropDown; // NOTE: backward compability to 1.4.3
                dijit.removeWaiState(this.focusNode,"activedescendant");
                dijit.setWaiState(this.textbox, "owns", popupId); // associate popup with textbox
            }
            // create a new query to prevent accidentally querying for a hidden
            // value from FilteringSelect's keyField
            var query = dojo.clone(this.query); // #5970
            this._lastInput = key; // Store exactly what was entered by the user.
            this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
            // #5970: set _lastQuery, *then* start the timeout
            // otherwise, if the user types and the last query returns before the timeout,
            // _lastQuery won't be set and their input gets rewritten
            this.searchTimer = setTimeout(dojo.hitch(this, function(query, _this){
                this.searchTimer = null;
                var fetch = {
                    queryOptions: {
                        ignoreCase: this.ignoreCase,
                        deep: true
                    },
                    query: query,
                    onBegin: dojo.hitch(this, "_setMaxOptions"),
                    onComplete: dojo.hitch(this, "_openResultList"),
                    onError: function(errText) {
                        _this._fetchHandle = null;
                        console.error('dijit.form.ComboBox: ' + errText);
                        _this.closeDropDown();
                    },
                    start: 0,
                    count: this.pageSize
                };
                dojo.mixin(fetch, _this.fetchProperties);
                this._fetchHandle = _this.store.fetch(fetch);

                var nextSearch = function(dataObject, direction) {
                    dataObject.start += dataObject.count * direction;
                    // #4091:
                    //      tell callback the direction of the paging so the screen
                    //      reader knows which menu option to shout
                    dataObject.direction = direction;
                    this._fetchHandle = this.store.fetch(dataObject);
                    this.focus();
                };
                this._nextSearch = this.dropDown.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
            }, query, this), this.searchDelay);
        },

        /* @Override */
        _getMenuLabelFromItem: function(/*Item*/ item) {
            var label = this.labelFunc(item, this.store);
            var labelType = this.labelType;
            // If labelType is not "text" we don't want to screw any markup ot whatever.
            if (this.highlightMatch != "none" && this.labelType == "text" && this._lastInput) {
                label = this.doHighlight(label, this._escapeHtml(this._lastInput));
                labelType = "html";
            }
    
            var icon = this.iconFunc(item, this.store);
            return {html: labelType == "html", label: label, icon: icon};
        },

        /* @Override */
        doHighlight: function(/*String*/label, /*String*/find) {
            // summary:
            //      Highlights the string entered by the user in the menu.  By default this
            //      highlights the first occurrence found. Override this method
            //      to implement your custom highlighting.
            // tags:
            //      protected

            // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
            var modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch === "all" ? "g": "");
            var i = this.queryExpr.indexOf("${0}");
    
            var escapedLabel = this._escapeHtml(label);
            find = dojo.regexp.escapeString(find); // escape regexp special chars

            var prefix = (i === 0) ? "^" : "";
            var suffix = (i === (this.queryExpr.length - 4)) ? "$" : "";
            var ret = escapedLabel.replace(
                // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
                new RegExp(prefix + "("+ find+ ")" + suffix, modifiers),
                    '<span class="dijitComboBoxHighlightMatch">$1</span>');

            return ret;// returns String, (almost) valid HTML (entities encoded)
        },

        /* @Override */
        _escapeHtml: function(/*String*/ str) {
            // TODO Should become dojo.html.entities(), when exists use instead
            // summary:
            //      Adds escape sequences for special characters in XML: &<>"'
            str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
                .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
            return str; // String
        },
    
        /* @Override */
        labelFunc: function(/*item*/ item, /*dojo.data.store*/ store) {
            // summary:
            //      Computes the label to display based on the dojo.data store item.
            // returns:
            //      The label that the ComboBox should display
            // tags:
            //      private
    
            // Use toString() because XMLStore returns an XMLItem whereas this
            // method is expected to return a String (#9354)
            return store.getValue(item, this.labelAttr || this.searchAttr).toString(); // String
        },

        iconFunc: function(/*item*/ item, /*dojo.data.store*/ store) {
            // summary:
            //      Computes the icon html text to display based on the dojo.data store item.
            // returns:
            //      The icon html text that the ComboBox should display
            // tags:
            //      private

            if (!this.iconClass) {
                return "";
            }

            var iconClass = this.iconClass(item, store) || "";
            var iconLabel = this.iconLabel(item, store) || "";
            var iconHtml = '<span class="dijitReset dijitInline dijitIcon ' + iconClass + '" dojoAttachPoint="iconNode">' + iconLabel + '</span>';

            return iconHtml; // String
        },

        iconClass: function(/*item*/ item, /*dojo.data.store*/ store) {
            // summary:
            //      Computes the icon CSS class to display based on the dojo.data store item.
            // returns:
            //      The icon html text that the ComboBox should display
            // tags:
            //      private

            return "";
        },

        iconLabel: function(/*item*/ item, /*dojo.data.store*/ store) {
            // summary:
            //      Get the label for icon display based on the dojo.data store item.
            // returns:
            //      The icon label text that the ComboBox should display
            // tags:
            //      private

            return "";
        }
    }
);

dojo.declare(
    "demo.widget._IconComboBoxMenu",
    dijit.form._ComboBoxMenu,
    {
        /* @Override */
        _createOption: function(/*Object*/ item, labelFunc) {
            // summary:
            //      Creates an option to appear on the popup menu subclassed by
            //      `dijit.form.FilteringSelect`.

            var menuitem = dojo.create("li", {
                "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
                role: "option"
            });
            var labelObject = labelFunc(item);
            if (labelObject.html) {
                menuitem.innerHTML = labelObject.icon + labelObject.label;
            } else {
                menuitem.innerHTML = labelObject.icon;
                menuitem.appendChild(
                    dojo.doc.createTextNode(labelObject.label)
                );
            }
            // #3250: in blank options, assign a normal height
            if (menuitem.innerHTML == "") {
                menuitem.innerHTML = "&nbsp;";
            }
            menuitem.item = item;
            return menuitem;
        }
    }
);